package interpreter.debugger.ui;

import interpreter.ByteCodeLoader;
import interpreter.debugger.DebugVirtualMachine;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;

/**
 * The class that takes care of the user interface
 *
 * @author Kris Selbekk
 */
public class UI {

    ByteCodeLoader bcl;
    DebugVirtualMachine dvm;
    HashMap<String, Command> commands;
    String[] currentArgs;

    /**
     * Constructor for UI
     *
     * Loads the DebugVirtualMachine object, initializes the command-map and
     * starts execution
     * @param dvm
     */
    public UI( DebugVirtualMachine dvm ) {
        this.dvm = dvm;
        startMap(); // Instantiates HashMap object and fills it with commands
        start();
    }

    /**
     * Starts the user interface - shows controls etc.
     * 
     */
    public void start() {

        System.out.println("Welcome to the debugger mode!");
        System.out.println("Source:");
        Command c = commands.get("src"); // Prints out help menu
        c.init(dvm);
        c.exec(null);
        c.print();
        while( true ) exec();  // Let's start the show
    }

    /**
     * Gets user command and executes said command
     */
    public void exec() {
        try {
            dvm.setRunning( true );
            while( dvm.isRunning() ) {
                Command c = getCommand();
                c.init( dvm );              // Initializes Command object
                c.exec( currentArgs );      // Execute Command
                c.print();                  // Print feedback, if any

                // Prints source when appropriate
                // displaySource( c );
            }
        } catch( Exception e ) { System.out.println("Exception caught: "+ e); } // remove for production
    }

    /**
     * Executes the command given by the user.
     *
     * This function collects command from user, finds the correct command object
     * and returns it to the calling function
     *
     * @return Command c The Command-instance requested
     */
    private Command getCommand() {
        Command command;
        while( true ) {
            // Waits for the user to enter command
            currentArgs = getInput().split("\\s");

            // Creates command object (if legit command)
            command = commands.get(currentArgs[0].toLowerCase());

            // If unrecognized
            if( command == null )
                System.out.println("Unrecognized command. Enter '?' for help");
            else break;
        }
        return command;
    }

    /**
     * Helper function, getting input from command line
     * @return whatever the user input was, or NULL if error
     */
    private String getInput() {
        InputStreamReader input = new InputStreamReader(System.in);
        BufferedReader reader = new BufferedReader(input);

        String command = null;

        try {
            System.out.print("> "); // Prompt
            command = reader.readLine();
        } catch( IOException io ) { System.out.println("IO exception found. Not good."); }

        return command;
    }
    
    /**
     * Instantiates the HashMap that will hold all commands
     *
     * To add a command, simply add the code, as well as an instance of the
     * correct Command subclass.
     *
     * Note that there are several "doubles" here - these would typically be
     * mentioned in the readme file for a wider choice for the user
     */
    private void startMap() {
        commands = new HashMap<String, Command>();
        try {
            commands.put("setb", SetBrkPtCommand.class.newInstance() );
            commands.put("set", commands.get("setb") );
            commands.put("clb", ClearBrkPtCommand.class.newInstance() );
            commands.put("clr", commands.get("clb") );
            commands.put("sin", StepInCommand.class.newInstance() );
            commands.put("si", commands.get("sin") );
            commands.put("stepin", commands.get("sin") );
            commands.put("so", StepOverCommand.class.newInstance() );
            commands.put("sover", commands.get("so") );
            commands.put("stepover", commands.get("so") );
            commands.put("sout", StepOutCommand.class.newInstance() );
            commands.put("stepout", commands.get("sout") );
            commands.put("func", DisplayFuncSrcCommand.class.newInstance() );
            commands.put("listb", ListBrkPtCommand.class.newInstance() );
            commands.put("c", ContinueCommand.class.newInstance() );
            commands.put("cont", commands.get("c") );
            commands.put("continue", commands.get("c") );
            commands.put("quit", HaltCommand.class.newInstance() );
            commands.put("halt", commands.get("quit") );
            commands.put("stop", commands.get("quit") );
            commands.put("exit", commands.get("quit") );
            commands.put("vars", DisplayLocalsCommand.class.newInstance() );
            commands.put("var", commands.get("vars") );
            commands.put("variables", commands.get("vars") );
            commands.put("variable", commands.get("vars") );
            commands.put("src", DisplaySrcCommand.class.newInstance() );
            commands.put("source", commands.get("src") );
            commands.put("?", HelpCommand.class.newInstance() );
            commands.put("help", commands.get("?") );
            commands.put("trace", TraceCommand.class.newInstance() );
        } catch( Exception e ) { /* Ignore exceptions for now */ }
    }
}
